Reasons for Redundant AsQueryable() Calls When Upgrading to .NET 6
As someone who writes .NET 6 directly, when looking at projects upgraded from .NET Core 3.1, you might see code like DbContext.Table.AsQueryable(). You might wonder why AsQueryable() is needed when DbSet<TEntity> already implements IQueryable<TEntity>, and in fact, removing it often seems to work fine. If you encounter this, check if the project has the System.Linq.Async package installed.
In versions prior to Microsoft.EntityFrameworkCore 5, the interfaces implemented by DbSet<TEntity> were as follows:
public abstract class DbSet<TEntity> : Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>, System.Collections.Generic.IAsyncEnumerable<TEntity>, System.Collections.Generic.IEnumerable<TEntity>, System.ComponentModel.IListSource, System.Linq.IQueryable<TEntity> where TEntity : classThe Queryable class defines extension methods Where() and Select() for IQueryable<TEntity>.
Meanwhile, the AsyncEnumerable class in System.Linq.Async defines extension methods Where() and Select() for IAsyncEnumerable<TSource>. This caused DbSet<TEntity> to be unable to determine which extension method to call, necessitating the use of AsQueryable() to change the declared type to IQueryable<TEntity>. Consequently, projects upgraded from older versions often retain many redundant AsQueryable() calls.
In Microsoft.EntityFrameworkCore 6 and later versions, the interfaces implemented by DbSet<TEntity> are as follows:
public abstract class DbSet<TEntity> : Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>, System.Collections.Generic.IEnumerable<TEntity>, System.ComponentModel.IListSource, System.Linq.IQueryable<TEntity> where TEntity : classTherefore, even if System.Linq.Async is installed, there will be no ambiguity regarding which method to call.
Changelog
- Initial version created.